import 'dart:convert';
import 'dart:io';

import 'package:bubbles/http/api/http_config.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';

import 'package:pretty_dio_logger/pretty_dio_logger.dart';

import '../models/api_response.dart';
import '../models/raw_data.dart';
import 'exception/exception.dart';
import 'interceptor/params_interceptor.dart';
import 'interceptor/token_expire_interceptor.dart';

class DioClient {
  ///超时时间
  static const int CONNECT_TIMEOUT = 15000;
  static const int RECEIVE_TIMEOUT = 15000;
  // static final DioClient _instance = DioClient._internal();
  // factory DioClient() => _instance;
  static DioClient get instance => DioClient._internal();

  late Dio dio;

  DioClient._internal() {
    // BaseOptions、Options、RequestOptions 都可以配置参数，优先级别依次递增，且可以根据优先级别覆盖参数
    BaseOptions options = BaseOptions(
      baseUrl: HttpConfig.baseUrl,
      connectTimeout: const Duration(milliseconds: CONNECT_TIMEOUT),
      // 响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: const Duration(milliseconds: RECEIVE_TIMEOUT),
      // Http请求头.
      headers: {},
    );

    dio = Dio(options);

    //       (dio.httpClientAdapter as IOHttpClientAdapter).onHttpClientCreate =
    //     (client) {
    //   client.badCertificateCallback = (cert, host, port) {
    //     return true; // 返回true强制通过
    //   };
    // };

    // 添加error拦截器
    // dio.interceptors.add(ErrorInterceptor());
    dio.interceptors.add(ParamsInterceptor());
    dio.interceptors.add(TokenExpireInterceptor());
    dio.interceptors.add(PrettyDioLogger(
      // 添加日志格式化工具类
      requestHeader: true,
      requestBody: true,
      responseBody: true,
      responseHeader: false,
      compact: false,
    ));
  }

  void init({
    String? baseUrl,
    int connectTimeout = CONNECT_TIMEOUT,
    int receiveTimeout = RECEIVE_TIMEOUT,
    Map<String, String>? headers,
    List<Interceptor>? interceptors,
  }) {
    dio.options = dio.options.copyWith(
      baseUrl: baseUrl,
      connectTimeout: Duration(milliseconds: connectTimeout),
      receiveTimeout: Duration(milliseconds: receiveTimeout),
      headers: headers ?? const {},
    );
    if (interceptors != null && interceptors.isNotEmpty) {
      dio.interceptors.addAll(interceptors);
    }
    print("url:$baseUrl");
  }

  Future<T?> request<T>(
    String url, {
    String method = "Get",
    Map<String, dynamic>? queryParameters,
    data,
    Map<String, dynamic>? headers,
    Function(ApiResponse<T>)? onResponse,
    bool Function(ApiException)? onError,
  }) async {
    try {
      Options options = Options()
        ..method = method
        ..headers = headers;

      data = _convertRequestData(data);

      Response response = await dio.request(url,
          queryParameters: queryParameters, data: data, options: options);

      return _handleResponse<T>(response, onResponse);
    } catch (e) {
      var exception = ApiException.from(e);
      if (onError?.call(exception) != true) {
        throw exception;
      }
    }

    return null;
  }

  _convertRequestData(data) {
    if (data != null) {
      data = jsonDecode(jsonEncode(data));
    }
    return data;
  }

  Future<T?> get<T>(
    String url, {
    Map<String, dynamic>? queryParameters,
    Map<String, dynamic>? headers,
    bool showLoading = true,
    Function(ApiResponse<T>)? onResponse,
    bool Function(ApiException)? onError,
  }) {
    return request(url,
        queryParameters: queryParameters,
        headers: headers,
        onResponse: onResponse,
        onError: onError);
  }

  Future<T?> post<T>(
    String url, {
    Map<String, dynamic>? queryParameters,
    data,
    Map<String, dynamic>? headers,
    bool showLoading = true,
    Function(ApiResponse<T>)? onResponse,
    bool Function(ApiException)? onError,
  }) {
    return request(url,
        method: "POST",
        queryParameters: queryParameters,
        data: data,
        headers: headers,
        onResponse: onResponse,
        onError: onError);
  }

  Future<T?> delete<T>(
    String url, {
    Map<String, dynamic>? queryParameters,
    data,
    Map<String, dynamic>? headers,
    bool showLoading = true,
    Function(ApiResponse<T>)? onResponse,
    bool Function(ApiException)? onError,
  }) {
    return request(url,
        method: "DELETE",
        queryParameters: queryParameters,
        data: data,
        headers: headers,
        onResponse: onResponse,
        onError: onError);
  }

  Future<T?> put<T>(
    String url, {
    Map<String, dynamic>? queryParameters,
    data,
    Map<String, dynamic>? headers,
    bool showLoading = true,
    Function(ApiResponse<T>)? onResponse,
    bool Function(ApiException)? onError,
  }) {
    return request(url,
        method: "PUT",
        queryParameters: queryParameters,
        data: data,
        headers: headers,
        onResponse: onResponse,
        onError: onError);
  }

  ///请求响应内容处理
  T? _handleResponse<T>(
    Response response,
    Function(ApiResponse<T>)? onResponse,
  ) {
    if (response.statusCode == 200) {
      if (T.toString() == (RawData).toString()) {
        RawData raw = RawData();
        raw.value = response.data;
        return raw as T;
      } else {
        ApiResponse<T> apiResponse = ApiResponse<T>.fromJson(response.data);
        onResponse?.call(apiResponse);
        return _handleBusinessResponse<T>(apiResponse);
      }
    } else {
      var exception =
          ApiException(response.statusCode, ApiException.unknownException);
      throw exception;
    }
  }

  ///业务内容处理
  T? _handleBusinessResponse<T>(ApiResponse<T> response) {
    if (response.isSuccess()) {
      return response.data;
    } else {
      var exception = ApiException(response.code, response.msg);
      throw exception;
    }
  }

//   /*
//    * 下载文件
//    */
//   downloadFile(urlPath, savePath) async {
//     Response response;
//     try {
//       response = await dio.download(urlPath, savePath,
//           onReceiveProgress: (int count, int total) {
//         //进度
//         // print("$count $total");
//       });
//       print('downloadFile success: ${response.data}');

//       return response.data;
//     } on DioException catch (e) {
//       print('downloadFile error: $e');
//       return Future.error(ParamsInterceptor.dioError(e));
//     }
// }
}
